package com.yuqih.common.servlet;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.Map;

import org.apache.commons.beanutils.PropertyUtils;
import org.bson.types.ObjectId;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;

/**
 * http请求的各种内容，主要为了记日志使用
 * 
 * @author yuqih
 *
 */
public class RequestContext implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	private String uri;
	
	private final ObjectId id;

	private final long requestTime;

	private long responseTime;

	private String serverName;

	private String contextPath;

	private String method;

	private String remoteIp;

	private Map<String, String[]> params;

	private Map<String, String[]> headers;

	private Map<String, Object> stateContents; // 与session,cookie等状态相关的内容

	private String requestBody;
	
	private String handlerClass;
	private String handlerMethod;

	private Integer responseStatus;

	@JsonIgnore
	private boolean responseBody;
	private Object responseObject;
	
	@JsonIgnore
	private Throwable error;

	/**
	 * 
	 */
	public RequestContext() {
		id = new ObjectId();
		requestTime = System.currentTimeMillis();
	}

	/**
	 * @param id
	 * @param requestTime
	 */
	RequestContext(ObjectId id, long requestTime) {
		super();
		this.id = id;
		this.requestTime = requestTime;
	}
	
	public static RequestContext newInstance(RequestContext other) throws Exception {
		RequestContext requestContext = new RequestContext(other.getId(), other.getRequestTime());
		PropertyUtils.copyProperties(requestContext, other);
		return requestContext;
	}
	
	/**
	 * @return the uri
	 */
	public String getUri() {
		return uri;
	}

	/**
	 * @param uri
	 *            the uri to set
	 */
	public void setUri(String uri) {
		this.uri = uri;
	}

	/**
	 * @return the id
	 */
	public ObjectId getId() {
		return id;
	}

	/**
	 * @return the requestTime
	 */
	public long getRequestTime() {
		return requestTime;
	}

	/**
	 * 消耗时间
	 * 
	 * @return
	 */
	@JsonProperty("cost_time")
	public long getCostTime() {
		if (responseTime == 0) {
			return System.currentTimeMillis() - requestTime;
		}
		return responseTime - requestTime;
	}

	/**
	 * @return the responseTime
	 */
	public long getResponseTime() {
		return responseTime;
	}

	/**
	 * @param responseTime
	 *            the responseTime to set
	 */
	public void setResponseTime(long responseTime) {
		this.responseTime = responseTime;
	}

	/**
	 * @return the serverName
	 */
	public String getServerName() {
		return serverName;
	}

	/**
	 * @param serverName
	 *            the serverName to set
	 */
	public void setServerName(String serverName) {
		this.serverName = serverName;
	}

	/**
	 * @return the contextPath
	 */
	public String getContextPath() {
		return contextPath;
	}

	/**
	 * @param contextPath
	 *            the contextPath to set
	 */
	public void setContextPath(String contextPath) {
		this.contextPath = contextPath;
	}

	

	/**
	 * @return the method
	 */
	public String getMethod() {
		return method;
	}

	/**
	 * @param method
	 *            the method to set
	 */
	public void setMethod(String method) {
		this.method = method;
	}

	/**
	 * @return the remoteIp
	 */
	public String getRemoteIp() {
		return remoteIp;
	}

	/**
	 * @param remoteIp
	 *            the remoteIp to set
	 */
	public void setRemoteIp(String remoteIp) {
		this.remoteIp = remoteIp;
	}

	/**
	 * @return the params
	 */
	public Map<String, String[]> getParams() {
		return params;
	}

	/**
	 * @param params
	 *            the params to set
	 */
	public void setParams(Map<String, String[]> params) {
		this.params = params;
	}

	/**
	 * @return the headers
	 */
	public Map<String, String[]> getHeaders() {
		return headers;
	}

	/**
	 * @param headers
	 *            the headers to set
	 */
	public void setHeaders(Map<String, String[]> headers) {
		this.headers = headers;
	}

	public Map<String, Object> getStateContents() {
		return stateContents;
	}

	public void setStateContents(Map<String, Object> stateContents) {
		this.stateContents = stateContents;
	}

	/**
	 * @return the requestBody
	 */
	public String getRequestBody() {
		return requestBody;
	}

	/**
	 * @param requestBody
	 *            the requestBody to set
	 */
	public void setRequestBody(String requestBody) {
		this.requestBody = requestBody;
	}

	/**
	 * @return the responseStatus
	 */
	public Integer getResponseStatus() {
		return responseStatus;
	}

	/**
	 * @param responseStatus
	 *            the responseStatus to set
	 */
	public void setResponseStatus(Integer responseStatus) {
		this.responseStatus = responseStatus;
	}

	

	/**
	 * @return the responseObject
	 */
	public Object getResponseObject() {
		return responseObject;
	}

	/**
	 * @param responseObject the responseObject to set
	 */
	public void setResponseObject(Object responseObject) {
		this.responseObject = responseObject;
	}

	/**
	 * @return the error
	 */
	public Throwable getError() {
		return error;
	}

	/**
	 * @param error
	 *            the error to set
	 */
	public void setError(Throwable error) {
		this.error = error;
	}

	/**
	 * @return
	 */
	public String getErrorMessage() {
		if (this.error == null) {
			return null;
		}
		
		ByteArrayOutputStream baous = new ByteArrayOutputStream();
		error.printStackTrace(new PrintStream(baous));
		
		try {
			return baous.toString(StandardCharsets.UTF_8.name());
		} catch (UnsupportedEncodingException e) {
			return null;
		}
	}

	/**
	 * @return the handlerClass
	 */
	public String getHandlerClass() {
		return handlerClass;
	}

	/**
	 * @param handlerClass the handlerClass to set
	 */
	public void setHandlerClass(String handlerClass) {
		this.handlerClass = handlerClass;
	}

	/**
	 * @return the handlerMethod
	 */
	public String getHandlerMethod() {
		return handlerMethod;
	}

	/**
	 * @param handlerMethod the handlerMethod to set
	 */
	public void setHandlerMethod(String handlerMethod) {
		this.handlerMethod = handlerMethod;
	}

	/**
	 * @return the responseBody
	 */
	public boolean isResponseBody() {
		return responseBody;
	}

	/**
	 * @param responseBody the responseBody to set
	 */
	public void setResponseBody(boolean responseBody) {
		this.responseBody = responseBody;
	}

}
